/***************************************************************************
 *   Copyleft 2008-2009 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#ifndef __VVERTEXBUFFER_H__
#define __VVERTEXBUFFER_H__

#include <Types/vTypes.h>
#include <Math/vMath.h>
#include "../Graphics/vColor.h"
#include "../../Utils/vUtils.h"
#include "../../Macros/vMemoryMacros.h"

namespace Vendetta {
	/*! \class vVertexBuffer
	 *
	 *  \brief This is an abstract class that provides an interface on top of various
	 *  implementations of vertex buffers or similar structures.
	 *
	 */
	class vVertexBuffer {
		friend class vVertexBufferManager;
	public:
		/*! \enum eVertexBufferPrimitiveType
		 *
		 *  \brief Specifies the type of primitives to be stored and rendered in vertex buffer.
		 *
		 */
		enum eVertexBufferPrimitiveType {
			VVBPT_QUADS,			//! Quads primitive type.
			VVBPT_TRIANGLES,		//! Triangle primitive type.
			VVBPT_TRIANGLE_STRIP	//! Triangle strip primitive type.

		};

		/*! \enum eVertexBufferFormat
		 *
		 *  \brief Specifies the format of vertex buffer.
		 *
		 */
		enum eVertexBufferFormat {
			VVBF_XYZ = 0x001,			//! Only vertices.
			VVBF_NXNYNZ = 0x002,		//! Only normals.
			VVBF_RGBA = 0x004,			//! Only colors.
			VVBF_UV = 0x008				//! Only texture coordinates.
		};

		/*! \enum eVertexBufferUsage
		 *
		 *  \brief Specifies the usage of vertex buffer.
		 *
		 */
		enum eVertexBufferUsage {
			VVBU_STATIC,			//! None changeable data.
			VVBU_DYNAMIC			//! Changeable data.
		};
	public:
		/*! \brief Locks the vertex buffer (usually for writing).
		 *    
		 * \return True if the lock request succeeded. False else.
		 */
		virtual vBool Lock();

		/*! \brief Unlocks the vertex buffer.      
		 *    
		 * \return True if the unlock request succeeded. False else.
		 */
		virtual vBool Unlock();

		/*! \brief Uses buffer for rendering.      
		 *    
		 */
		virtual vVoid Use() = 0;

		/*! \brief Determines if the vertex buffer is locked or not.      
		 * 
		 * \return boolean value specifying if vertex buffer is locked or not.
		 */
		virtual const vBool IsLocked() const;

		/*! \brief Specifies untransformed vertex data. 
		 * 
		 * Should be last call for each vertex. That is after the normal, color, and
		 * texture coordinate calls are made for the vertex.
		 * Note: should be used when format includes VVBF_XYZ.
		 * Param should be created using new operator, because it would be destroyed 
		 * by vVertexBuffer;
		 * Only valid when buffer is not locked.
		 *
		 * \param uiInd Index in vertex buffer to insert.
		 * \param x x coordinate.
		 * \param y y coordinate.
		 * \param z z coordinate.
		 * \return True if set, false otherwise (maybe locked).
		 */
		virtual vBool SetVertex(vUInt uiInd, vReal x, vReal y, vReal z);

		/*! \brief Specifies untransformed vertex data. 
		 * 
		 * Should be last call for each vertex. That is after the normal, color, and
		 * texture coordinate calls are made for the vertex.
		 * Note: should be used when format includes VVBF_XYZ.
		 * Param should be created using new operator, because it would be destroyed 
		 * by vVertexBuffer;
		 * Only valid when buffer is not locked.
		 *
		 * \param uiInd Index in vertex buffer to insert.
		 * \param vertex vPoint3D coordinates.
		 * \return True if set, false otherwise (maybe locked).
		 */
		virtual vBool SetVertex(vUInt uiInd, const vPoint3D& vertex);

		/*! \brief Specifies untransformed vertices data. 
		 * 
		 * Should be last call for each vertex. That is after the normal, color, and
		 * texture coordinate calls are made for the vertex.
		 * Note: should be used when format includes VVBF_XYZ.
		 * Param should be created using new operator, because it would be destroyed 
		 * by vVertexBuffer;
		 * Only valid when buffer is not locked.
		 *
		 * \param vertices vArray<vPoint3D> coordinates.
		 * \return True if set, false otherwise (maybe locked or size is 0).
		 */
		virtual vBool SetVertices(const vArray<vPoint3D>& vertices);

		/*! \brief Specifies untransformed vertices data. 
		 * 
		 * Should be last call for each vertex. That is after the normal, color, and
		 * texture coordinate calls are made for the vertex.
		 * Note: should be used when format includes VVBF_XYZ.
		 * Param should be created using new operator, because it would be destroyed 
		 * by vVertexBuffer;
		 * Only valid when buffer is not locked.
		 *
		 * \param vertices vPoint3D* array coordinates.
		 * \param vertexCount Number of array vertices.
		 * \return True if set, false otherwise (maybe locked or size is 0).
		 */
		virtual vBool SetVertices(const vPoint3D* vertices, vUInt vertexCount);

		/*! \brief Specifies texture vertex coordinates.
		 * 
		 * Successive calls should be made to this
		 * method if more than one set of texture coordinates need to be 
		 * specified.
		 * Note: should be used when format includes VVBF_UV.
		 * Only valid when buffer is not locked.
		 *
		 * \param uiInd Index in vertex buffer to insert.
		 * \param u U texture coordinate.
		 * \param v V texture coordinate.
		 * \return True if set, false otherwise (maybe locked).
		 */
		virtual vBool SetTexCoord(vUInt uiInd, vReal u, vReal v);

		/*! \brief Specifies texture vertex coordinates.
		 * 
		 * Successive calls should be made to this
		 * method if more than one set of texture coordinates need to be 
		 * specified.
		 * Note: should be used when format includes VVBF_UV.
		 * Only valid when buffer is not locked.
		 *
		 * \param uiInd Index in vertex buffer to insert.
		 * \param texCoord U, V texture coordinates.
		 * \return True if set, false otherwise (maybe locked).
		 */
		virtual vBool SetTexCoord(vUInt uiInd, const vPoint2D& texCoord);

		/*! \brief Specifies texture vertex coordinates.
		 * 
		 * Successive calls should be made to this
		 * method if more than one set of texture coordinates need to be 
		 * specified.
		 * Note: should be used when format includes VVBF_UV.
		 * Only valid when buffer is not locked.
		 *
		 * \param texCoords vArray<vPoint2D> array texture coordinates.
		 * \return True if set, false otherwise (maybe locked or size is 0).
		 */
		virtual vBool SetTexCoords(const vArray<vPoint2D>& texCoords);

		/*! \brief Specifies texture vertex coordinates.
		 * 
		 * Successive calls should be made to this
		 * method if more than one set of texture coordinates need to be 
		 * specified.
		 * Note: should be used when format includes VVBF_UV.
		 * Only valid when buffer is not locked.
		 *
		 * \param texCoords vPoint2D* array texture coordinates.
		 * \param texCoordCount Number of array texture coordinates.
		 * \return True if set, false otherwise (maybe locked or size is 0).
		 */
		virtual vBool SetTexCoords(const vPoint2D* texCoords, vUInt texCoordCount);

		/*! \brief Specifies vertex normal data.
		 *
		 * Note: should be used when format includes VVBF_NXNYNZ.
		 * Only valid when buffer is not locked.
		 *
		 * \param uiInd Index in vertex buffer to insert.
		 * \param nx Normal x value.
		 * \param ny Normal y value.
		 * \param nz Normal z value.
		 * \return True if set, false otherwise (maybe locked).
		 */
		virtual vBool SetNormal(vUInt uiInd, vReal nx, vReal ny, vReal nz);

		/*! \brief Specifies vertex normal data.
		 *
		 * Note: should be used when format includes VVBF_NXNYNZ.
		 * Only valid when buffer is not locked.
		 *
		 * \param uiInd Index in vertex buffer to insert.
		 * \param normal Normal vPoint3D value.
		 * \return True if set, false otherwise (maybe locked).
		 */
		virtual vBool SetNormal(vUInt uiInd, const vPoint3D& normal);

		/*! \brief Specifies vertex normal data.
		 *
		 * Note: should be used when format includes VVBF_NXNYNZ.
		 * Only valid when buffer is not locked.
		 *
		 * \param normals Normals vArray<vPoint3D> values.
		 * \return True if set, false otherwise (maybe locked or size is 0).
		 */
		virtual vBool SetNormals(const vArray<vPoint3D>& normals);

		/*! \brief Specifies vertex normal data.
		 *
		 * Note: should be used when format includes VVBF_NXNYNZ.
		 * Only valid when buffer is not locked.
		 *
		 * \param normals Normals vPoint3D* array values.
		 * \param normalCount Number of array normals.
		 * \return True if set, false otherwise (maybe locked or size is 0).
		 */
		virtual vBool SetNormals(const vPoint3D* normals, vUInt normalCount);

		/*! \brief Specifies diffuse color data.
		 * 
		 * Note: should be used when format includes VVBF_RGBA.
		 * Only valid when buffer is not locked.
		 *
		 * \param uiInd Index in vertex buffer to insert.
		 * \param r Red color value.
		 * \param g Green color value.
		 * \param b Blue color value.
		 * \param a Alpha color value.
		 * \return True if set, false otherwise (maybe locked).
		 */
		virtual vBool SetColor(vUInt uiInd, vReal r, vReal g, vReal b, vReal a);

		/*! \brief Specifies diffuse color data.
		 * 
		 * Note: should be used when format includes VVBF_RGBA.
		 * Only valid when buffer is not locked.
		 *
		 * \param uiInd Index in vertex buffer to insert.
		 * \param color vColor value.
		 * \return True if set, false otherwise (maybe locked).
		 */
		virtual vBool SetColor(vUInt uiInd, const vColor& color);

		/*! \brief Specifies diffuse color data.
		 * 
		 * Note: should be used when format includes VVBF_RGBA.
		 * Only valid when buffer is not locked.
		 *
		 * \param colors vArray<vColor> values.
		 * \return True if set, false otherwise (maybe locked or size is 0).
		 */
		virtual vBool SetColors(const vArray<vColor>& colors);

		/*! \brief Specifies diffuse color data.
		 * 
		 * Note: should be used when format includes VVBF_RGBA.
		 * Only valid when buffer is not locked.
		 *
		 * \param colors vColor* array values.
		 * \param colorCount Number of array colors.
		 * \return True if set, false otherwise (maybe locked or size is 0).
		 */
		virtual vBool SetColors(const vColor* colors, vUInt colorCount);

		/*! \brief Return vertex count.
		 * 
		 * \return Vertex count.
		 */
		inline vUInt VertexCount() const {
			return mVertexCount;
		}

		/*! \brief Return normal count.
		 * 
		 * \return Normal count.
		 */
		inline vUInt NormalCount() const {
			return mNormalCount;
		}

		/*! \brief Return texture coordinates count.
		 * 
		 * \return TexCoord count.
		 */
		inline vUInt TexCoordCount() const {
			return mTextCoordCount;
		}

		/*! \brief Return color count.
		 * 
		 * \return Color count.
		 */
		inline vUInt ColorCount() const {
			return mColorCount;
		}

	protected:
		/*! \brief Builds buffer.      
		 *    
		 *	\return True if build successful, false otherwise.
		 */
		virtual vBool build() = 0;

		/*! \brief Default constructor.      
		 *    
		 */
		vVertexBuffer(vUInt uiSize, vUInt32 ui32Format = VVBF_XYZ, 
			eVertexBufferPrimitiveType ePrimitiveType = VVBPT_TRIANGLES,
			eVertexBufferUsage eUsage = VVBU_DYNAMIC): 
			mpVertices(vNull), mpNormals(vNull), mpTexCoords(vNull), mpColors(vNull), mVertexCount(uiSize),
			mNormalCount(uiSize), mTextCoordCount(uiSize), mColorCount(uiSize), mLocked(false), mFormat(ui32Format), 
			mUsage(eUsage), mPrimitiveType(0) { }


		/*! \brief Destructor.      
		 *    
		 */
		virtual ~vVertexBuffer();

	protected:
		//! Disabled - if You call this You get exception.
		vVertexBuffer();

		//! Disabled - if You call this You get exception.
		vVertexBuffer(const vVertexBuffer& oGlVertexBuffer);

		//! Disabled - if You call this You get exception.
		vVertexBuffer& operator=(const vVertexBuffer& oGlVertexBuffer);

	protected:
		vPoint3D* mpVertices;
		vPoint3D* mpNormals;
		vPoint2D* mpTexCoords;
		vColor*	mpColors;

		vUInt mVertexCount;
		vUInt mNormalCount;
		vUInt mTextCoordCount;
		vUInt mColorCount;

		vUInt32 mFormat;
		vUInt32 mPrimitiveType;
		eVertexBufferUsage mUsage;

	private:
		vBool mLocked;		
	};
}
#endif // __VVERTEXBUFFER_H__